/*
 * Copyright (c) 2013-2018 上海汇数数据. All rights reserved.
 * @(#) PbeCipherApi.java 2018-08-06 17:26
 */

package cn.jh.common.core.crypto;

import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;


/**
 * @author Fuchun
 * @since 1.0
 */
public class PbeCipherApi extends SymmetricCipherApi {

    private static final Logger pbeLog = LoggerFactory.getLogger("PBE");

    /**
     * 默认的密钥加密密码。
     */
    public static final String DEFAULT_PASSWORD = "00000000";

    /**
     * 默认的PBE算法名称（PBEWithMD5AndDES）。
     */
    public static final String MD5_AND_DES = "PBEWithMD5AndDES";

    /**
     * PBEWithSHA1AndDESede 算法转换名称。
     */
    public static final String SHA1_AND_DESEDE = "PBEWithSHA1AndDESede";

    /**
     * PBEWithSHA1AndRC2_40 算法转换名称。
     */
    public static final String SHA1_AND_RC2_40 = "PBEWithSHA1AndRC2_40";

    private static final String SALT_SEQUENCE = "~!@#$%^&*()_+`1234567890-=[]\\{}|;':\",./<>?";

    private static String randomSalt(int length) {
        StringBuilder b = new StringBuilder();
        int index;
        for (int i = 0; i < length; i++) {
            index = (int) (Math.random() * SALT_SEQUENCE.length());
            b.append(SALT_SEQUENCE.charAt(index));
        }
        return b.toString();
    }

    @NotNull
    public static Key md5AndDesKey(String password) {
        return passwordToKey(password, MD5_AND_DES);
    }

    @NotNull
    public static Key sha1AndDESede(String password) {
        return passwordToKey(password, SHA1_AND_DESEDE);
    }

    @NotNull
    public static Key sha1AndRc2(String password) {
        return passwordToKey(password, SHA1_AND_RC2_40);
    }

    @NotNull
    public static Key passwordToKey(String password, String... algorithms) {
        String passwd;
        if (password == null || (passwd = password.trim()).isEmpty()) {
            // using default password.
            passwd = DEFAULT_PASSWORD;
        }
        String algorithm = MD5_AND_DES; // default algorithm: PBEWithMD5AndDES
        if (algorithms != null && algorithms.length >= 1) {
            algorithm = algorithms[0];
        }
        PBEKeySpec keySpec = new PBEKeySpec(passwd.toCharArray());
        try {
            SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm);
            return skf.generateSecret(keySpec);
        } catch (NoSuchAlgorithmException ex) {
            pbeLog.warn("Not support `" + algorithm + "` algorithm with PEB on this platform.");
            throw new RuntimeException(ex);
        } catch (InvalidKeySpecException ex) {
            pbeLog.warn("PBEKeySpec keySpec is of - " + ex.getMessage());
            throw new RuntimeException(ex);
        }
    }

    public static PbeCipherApi md5AndDes() {
        return new PbeCipherApi(CipherParamsKt.pbe(MD5_AND_DES));
    }

    public static PbeCipherApi sha1AndDESede() {
        return new PbeCipherApi(CipherParamsKt.pbe(SHA1_AND_DESEDE));
    }

    public static PbeCipherApi sha1AndRC2() {
        return new PbeCipherApi(CipherParamsKt.pbe(SHA1_AND_RC2_40));
    }

    private PbeCipherApi(SymmetricCipherParams params) {
        super(params);
    }

    @Override
    protected int ensureIvSize() {
        return ((SymmetricCipherParams) params).getIvSize();
    }

    @Override
    protected AlgorithmParameterSpec toParameterSpec(byte[] parameters) {
        return new PBEParameterSpec(parameters, 100);
    }
}
